package com.litchishell.feartures.main;

import com.litchishell.feartures.pojo.Dish;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
 * @author LitchiShell
 * @description: 流（Stream）：从支持数据处理操作的源生成的元素序列
 * 源：比如集合、数组或I/O资源
 * @create 2021-11-21
 */
public class StreamTest {
    public static void main(String[] args) {
        List<Dish> menu = Arrays.asList(
                new Dish("pork",false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        );
        List<String> collect1 = menu.stream()
                // 过滤卡路里大于300的Dish
                .filter(dish -> dish.getCalories() > 300)
                // 获取上面筛选结果的名称
                .map(Dish::getName)
                // 获取上面结果的前3个
                .limit(3)
                // 转换为对应的List集合
                .collect(toList());
        System.out.println(collect1);

        long count = menu.stream()
                .filter(dish -> dish.getCalories() > 300)
                .distinct()
                .limit(3)
                .count();
        System.out.println(count);

        // 流的基本操作
        /**
         * 1.筛选
         */
        // 用谓词筛选 predicate--->test--->返回一个boolean的值
        List<Dish> vegetarianMenu = menu.stream()
                .filter(Dish::isVegetarian)
                .collect(toList());

        // 筛选各异的元素
        List<Integer> integers = Arrays.asList(1, 2, 3, 3, 3, 4, 4, 2, 6);
        List<Integer> integers1 = integers.stream()
                .filter(v -> v % 2 == 0)
                .distinct()
                .collect(toList());
        integers1.forEach(System.out::println);

        // 2.流的切片 Java9中的takeWhile、dropWhile
        List<Dish> specialMenu = Arrays.asList(
                new Dish("pork",false, 120, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 200, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 240, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        );
        // takeWhile
        List<Dish> collect = specialMenu.stream()
                .takeWhile(dish -> dish.getCalories() < 300)
                .collect(toList());
        collect.forEach(System.out::println);

        // dropWhile
        List<Dish> collect2 = specialMenu.stream()
                .dropWhile(dish -> dish.getCalories() < 300)
                .collect(toList());
        collect2.forEach(System.out::println);

        // limit  limit和skip互补
        List<Dish> collect3 = specialMenu.stream()
                .dropWhile(dish -> dish.getCalories() < 300)
                .limit(2)
                .collect(toList());
        collect3.forEach(System.out::println);

        // skip
        List<Dish> collect4 = specialMenu.stream()
                .dropWhile(dish -> dish.getCalories() < 300)
                .skip(2)
                .collect(toList());
        collect4.forEach(System.out::println);

        // 3.映射map、flatMap
        // map
        List<String> collect5 = menu.stream()
                .map(Dish::getName)
                .collect(toList());
        System.out.println(collect5);

        // flatMap 扁平化流
        List<String> strings = Arrays.asList("Hello", "world");
        List<String> collect6 = strings.stream()
                .map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(toList());
        System.out.println(collect6);

        List<Integer> integers2 = Arrays.asList(1, 2, 3);
        List<Integer> integers3 = Arrays.asList(3, 4);
        List<int[]> collect7 = integers2.stream()
                .flatMap(i ->
                        integers3.stream()
                                .map(j -> new int[]{i, j})
                )
                .collect(toList());
        collect7.forEach(System.out::println);

        List<int[]> collect8 = integers2.stream()
                .flatMap(i ->
                        integers3.stream()
                                .filter(j -> (j+i) % 3 == 0)
                                .map(j -> new int[]{i, j})
                )
                .collect(toList());
        System.out.println(collect8);;

        // 4.查找allMatch、anyMatch、noneMatch、findFirst、findAny

        // anMatch 至少匹配一个元素的谓词 终端操作
        if (menu.stream().anyMatch(Dish::isVegetarian)) {
            System.out.println("Vegetarian");
        }

        // allMatch 是否全部符合条件的谓词 终端操作
        if (menu.stream().allMatch(dish -> dish.getCalories() < 1000)) {
            System.out.println("Healthy");
        }

        // noneMatch 与allMatch相对
        if (menu.stream().noneMatch(dish -> dish.getCalories() > 1000)) {
            System.out.println("Healthy");
        }

        // findAny 返回当前流中任意元素
        Optional<Dish> any = menu.stream()
                .filter(Dish::isVegetarian)
                .findAny();
        any.ifPresent(dish -> System.out.println(dish.getName()));

        // findFirst 查找第一个元素
        List<Integer> integers4 = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> first = integers4.stream()
                .map(n -> n * n)
                .filter(n -> n % 3 == 0)
                .findFirst();

        // 5.归约（将流归约成一个值）reduce
        List<Integer> integers5 = Arrays.asList(1, 2, 3, 4, 5, 4, 7);
        Integer reduce = integers5.stream()
                .reduce(0, (a, b) -> a + b);
        Integer reduce2 = integers5.stream().reduce(0, Integer::sum);
        Optional<Integer> reduce3 = integers5.stream().reduce((a, b) -> a + b);
        System.out.println(reduce);
        System.out.println(reduce2);
        reduce3.ifPresent(i -> System.out.println(i));

        List<BigDecimal> bigDecimals = Arrays.asList(new BigDecimal(1), new BigDecimal(2), new BigDecimal(3));
        Optional<BigDecimal> reduce1 = bigDecimals.stream()
                .reduce(BigDecimal::min);
        reduce1.ifPresent(r -> System.out.println(r));

        // 基本类型流特化 IntStream、DoubleStream、LongStream
        // 将流转换为特化流 mapToInt mapToDouble mapToLong
        int sum = menu.stream()
                .mapToInt(Dish::getCalories)
                .sum();
        System.out.println(sum);

        // 特化流转流
        IntStream intStream = menu.stream()
                .mapToInt(Dish::getCalories);
        Stream<Integer> stream = intStream.boxed();

        // optional版的特化流OptionalInt OptionalDouble OptionalLong
        OptionalInt max = menu.stream()
                .mapToInt(Dish::getCalories)
                .max();
        int i = max.orElse(1);

        //  数值范围
        // rangeClosed 包含结束值
        IntStream intStream1 = IntStream.rangeClosed(1, 100)
                .filter(n -> n % 2 == 0);
        long count1 = intStream1.count();
        System.out.println(count1);

        // range 不包含结束值
        IntStream intStream2 = IntStream.range(1, 100)
                .filter(n -> n % 2 == 0);
        long count2 = intStream2.count();
        System.out.println(count2);

    }
}
